package io.gitee.chearnee.fw.web.advice;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.gitee.chearnee.fw.common.model.vo.ResultVo;
import io.gitee.chearnee.fw.web.annotation.EnableResponseAdvice;
import io.gitee.chearnee.fw.web.config.WebEndpointConfiguration;
import io.gitee.chearnee.fw.web.page.ResultPage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.lang.reflect.ParameterizedType;

/**
 * @author NN
 */
@Slf4j
@RestControllerAdvice
public class ResultBodyAdvice implements ResponseBodyAdvice<Object> {

    private final ObjectMapper objectMapper;
    private final WebEndpointConfiguration webEndpointConfiguration;

    public ResultBodyAdvice(final ObjectMapper objectMapper, ObjectProvider<WebEndpointConfiguration> webEndpointConfigurations) {
        this.objectMapper = objectMapper;
        this.webEndpointConfiguration = webEndpointConfigurations.getIfAvailable();
    }

    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
        boolean isResponse;
        if (methodParameter.getGenericParameterType() instanceof ParameterizedType) {
            Class<?> rawType = (Class<?>) ((ParameterizedType) methodParameter.getGenericParameterType()).getRawType();
            isResponse = ResultVo.class.isAssignableFrom(rawType);
            return !isResponse;
        } else {
            Class<?> genericParameterType = (Class<?>) methodParameter.getGenericParameterType();
            isResponse = ResultVo.class.isAssignableFrom(genericParameterType);
            if (isResponse) {
                return false;
            }
        }
        EnableResponseAdvice enableResponseAdvice = methodParameter.getDeclaringClass().getAnnotation(EnableResponseAdvice.class);
        if (enableResponseAdvice == null) {
            enableResponseAdvice = methodParameter.getMethodAnnotation(EnableResponseAdvice.class);
        }
        return enableResponseAdvice == null || enableResponseAdvice.enable();
    }

    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        if (webEndpointConfiguration != null && webEndpointConfiguration.isEndpointUri(serverHttpRequest)) {
            return o;
        }
        if (methodParameter.getGenericParameterType().equals(String.class)) {
            String s;
            try {
                s = objectMapper.writeValueAsString(ResultVo.success(o));
            } catch (JsonProcessingException e) {
                return ResultVo.fail("转换失败");
            }
            serverHttpResponse.getHeaders().setContentType(MediaType.APPLICATION_JSON);
            return s;
        }
        if (o instanceof IPage<?> iPage) {
            return ResultVo.success(ResultPage.restPage(iPage));
        }
        return ResultVo.success(o);
    }
}
